Introduction

This notebook is a demonstration of the R package ReacTran. It is a direct implementation of the code and models described in this vignette provided by the package authors, (Soetaert and Meysman). However the plots are implemented using ggplot2, with plotly used for some 3-d examples. The plotting functions included in the package use base R. The purpose of this notebook is to demonstrate the use of modern plotting libraries with the output of the ReacTran package.

Setup

library(ReacTran)
library(tidyverse)
theme_set(theme_bw())
library(patchwork)
library(metR)
library(plotly)

2 A parabolic PDE

As an example of the parabolic type, consider the 1-D diffusion-reaction model, in spherical, cylindrical and cartesian coordinates, defined for \(r\) in [0, 10]: \[ \frac{\partial C}{\partial t} = \frac{\partial}{\partial r}\left(D \cdot \frac{\partial C}{\partial r} \right) - Q \] with \(t\) the time, \(r\) the (radial) distance from the origin, \(Q\), the consumption rate, and with boundary conditions (values at the model edges): \[ \frac{\partial C}{\partial r}_{r=0} = 0 \] \[ C_{r=10} = Cext \]

To solve this model in R, first the 1-D model Grid is defined; it divides 10 cm (L) into 1000 equally-sized boxes (N).

Grid <- setup.grid.1D(N = 1000, L = 10)

Next the properties r and r^2 are defined on this grid:

r <- setup.prop.1D(grid = Grid, func = function(r) r)
r2 <- setup.prop.1D(grid = Grid, func = function(r) r^2)

The model equation includes a transport term, approximated by ReacTran function tran.1D and a consumption term (Q); only the downstream boundary condition, prescribed as a concentration (C.down) needs to be specified, as the zero-gradient at the upstream boundary is the default:

pde1D <- function(t, C, parms, A = 1) {
  tran <- tran.1D(C = C, A = A, D = D, C.down = Cext, dx = Grid)$dC
  
  list(tran - Q)
}

The model parameters are defined:

D <- 1
Q <- 1
Cext <- 20

2.1. Steady-state solution

In a first application, the model is solved to steady-state, which retrieves the condition where the concentrations are invariant, e.g. for the cylindrical coordinate case: \[ 0 = \frac{1}{r^2} \cdot \frac{\partial}{\partial r}\left(r^2 \cdot D \cdot \frac{\partial C}{\partial r} \right) - Q \]

In R , steady-state conditions can be estimated using functions from package rootSolve which implement amongst others a Newton-Raphson algorithm. For 1-dimensional models, steady.1D should be used. The initial “guess” of the steady-state solution (y) is unimportant; here we take simply N random numbers. Argument nspec = 1 informs the solver that only one component is described:

library(rootSolve)
Cartesian <- steady.1D(y = runif(Grid$N), func = pde1D, parms = NULL, nspec = 1, A = 1)
Cylindrical <- steady.1D(y = runif(Grid$N), func = pde1D, parms = NULL, nspec = 1, A = r)
print(system.time(
  Spherical <- steady.1D(y = runif(Grid$N), func = pde1D, parms = NULL, nspec = 1, A = r2)
))
   user  system elapsed 
  0.003   0.000   0.004 

Plot using ggplot2:

data.frame(
  x = Grid$x.mid,
  cartesian = Cartesian$y,
  cylindrical = Cylindrical$y,
  spherical = Spherical$y
) %>% 
  pivot_longer(cols = !contains("x"), names_to = "coord", values_to = "y") %>% 
  ggplot() +
  geom_line(aes(x, y, color = coord, linetype = coord),
            size = 1) +
  scale_x_continuous(breaks = seq(0, 10, 2)) +
  labs(y = "C", color = NULL, linetype = NULL,
       title = "Figure 1: Steady-state solution of the 1-D diffusion-reaction model") +
  theme(panel.grid = element_blank(),
        legend.position = c(1, 0),
        legend.justification = c(1, 0),
        legend.background = element_blank())

2.2. The method of lines

Next the model (for spherical coordinates) is run dynamically for 100 time units using deSolve function ode.1D, and starting with an initially uniform distribution (y = rep(1, Grid$N)):

require(deSolve)
times <- seq(0, 100, by = 1)
system.time(
  out <- ode.1D(y = rep(1, Grid$N), times = times, func = pde1D, parms = NULL, nspec = 1, A = r2)
)
   user  system elapsed 
  0.269   0.000   0.283 

Here, out is a matrix, whose 1st column contains the output times, and the next columns the values of the state variables in the different boxes:

tail(out[, 1:4], n = 3)
       time        1        2        3
 [99,]   98 3.332278 3.332303 3.332366
[100,]   99 3.332383 3.332408 3.332471
[101,]  100 3.332478 3.332503 3.332566

Plot using ggplot2:

# Create a data frame of grid values for joining to the output:
grid_x.mid <- data.frame(
  X = paste0("X", 1:length(Grid$x.mid)),
  grid = Grid$x.mid
)

# Pivot the output to longer format and join with the above data frame to add the Distance values:
out_long <- data.frame(out) %>% 
  pivot_longer(cols = starts_with("X"), names_to = "X", values_to = "density") %>%
  left_join(grid_x.mid, by = "X")

# Plot:
ggplot(out_long) +
  geom_raster(aes(time, grid, fill = density),
             interpolate = TRUE, show.legend = FALSE) +
  geom_contour(aes(time, grid, z = density), color = "black", alpha = 0.5) +
  geom_text_contour(aes(time, grid, z = density), skip = 0, nudge_y = 0.1) +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_continuous(breaks = seq(0, 10, 2)) +
  scale_fill_distiller(palette = "Spectral",
                       breaks = seq(0, 1, 0.2)) +
  labs(x = "Time (days)", y = "Distance (cm)", fill = NULL,
       title = "Figure 2: Dynamic solution of the 1-D diffusion-reaction model") +
  coord_equal(expand = F, ratio = 10)

Add a 3-d plot using plotly:

out_3d <-
  list(
    x = times,
    y = grid_x.mid,
    z = t(out[ , -1])) # need to transpose this to match the x as times and y as r

# Plot:
my_palette <- rev(colorRampPalette(RColorBrewer::brewer.pal(9, "Spectral"))(40)) # ColorBrewer Spectral palette
plot_ly(x = out_3d$x, y = out_3d$y, z = out_3d$z, colors = my_palette) %>%
  add_surface() %>%
  layout(
    scene = list(
      camera=list(
        eye = list(x=0, y=-2, z=1)
        )
    )
  )

3 A hyperbolic PDE

The equation for a wave travelling in one direction (\(x\)) is given by: \[ \frac{\partial ^2 u}{\partial t^2} = c^2 \frac{\partial ^2 u}{\partial x^2} \]

where \(c\) is the propagation speed of the wave, and \(u\) is the variable that changes as the wave passes. This equation is second-order in both \(t\) and \(x\). The wave equation is the prototype of a “hyperbolic” partial differential equation.

For it to be solved in R, the equation is rewritten as two coupled equations, first-order in time:

\[ \frac{\partial u_1}{\partial t} = u_2 \]

\[ \frac{\partial u_2}{\partial t} = c^2 \frac{\partial ^2 u_1}{\partial x^2} \]

We solve the equation with the following initial and boundary conditions: \[ u_1(0, x) = \exp^{-0.05 x^2} \] \[ u_2(0, x) = 0 \] \[ u_{t, - \infty} = 0 \] \[ u_{t, \infty} = 0 \] where the first condition represents a Gaussian pulse.

Here, the grid extends from -100 to 100:

dx <- 0.2
xgrid <- setup.grid.1D(-100, 100, dx.1 = dx)
x <- xgrid$x.mid
N <- xgrid$N

The initial condition, yini and output times are defined next:

uini <- exp(-0.05 * x^2)
vini <- rep(0, N)
yini <- c(uini, vini)
times <- seq (from = 0, to = 50, by = 1)

The wave equation derivative function first extracts, from state variable vector y the two properties u1, u2, both of length N, after which ReacTran function tran.1D performs transport of u1. The squared velocity (\(c^2\)) is taken as 1 (D = 1):

wave <- function (t, y, parms) {
  u1 <- y[1:N]
  u2 <- y[-(1:N)]
  du1 <- u2
  du2 <- tran.1D(C = u1, C.up = 0, C.down = 0, D = 1, dx = xgrid)$dC
  
  return(list(c(du1, du2)))
}

The wave equation can be solved efficiently with a non-stiff solver such as the Runge-Kutta method ode45:

out <- ode.1D(func = wave, y = yini, times = times, parms = NULL, nspec = 2, method = "ode45", dimens = N, names = c("u", "v"))

Plot using ggplot2:

iout <- seq(11, length(times), by = 10)
data.frame(out[c(1, iout), 1:(N+1)]) %>% 
  pivot_longer(cols = contains("X"), names_to = "X", values_to = "u") %>% 
  mutate(x = rep(x, 6)) %>% 
  ggplot() +
  geom_line(aes(x, u, color = factor(time), linetype = factor(time))) +
  scale_x_continuous(breaks = seq(-40, 40, 20)) +
  scale_y_continuous(breaks = seq(0, 1, 0.2)) +
  ggsci::scale_color_d3(name = "time",
                        breaks = c(1, iout) - 1,
                        labels = paste0("t = ", c(1, iout) - 1)) +
  scale_linetype_discrete(name = "time",
                          breaks = c(1, iout) - 1,
                          labels = paste0("t = ", c(1, iout) - 1)) +
  coord_cartesian(xlim = c(-50, 50)) +
  ggtitle("Figure 3: The 1-D wave equation") +
  theme(panel.grid = element_blank())

Plot a 3-d version using plotly:

out_3d <-
  list(
    x = times,
    y = grid_x.mid,
    z = t(out[, 2:(N+1)])) # need to transpose this to match the x as times and y as r

# Plot:
plot_ly(x = out_3d$x, y = out_3d$y, z = out_3d$z, colors = my_palette) %>%
  add_surface()

4 An elliptic PDE

The final example describes again a diffusion-reaction system with production \(p\), consumption \(rC\), and diffusive transport (diffusion coefficients \(Dx\), \(Dy\)) of a substance \(C\) in 2 dimensions \((x, y)\); the boundaries are prescribed as zero-gradient (the default): \[ \frac{\partial C}{\partial t} = \frac{\partial}{\partial x}[D_x \cdot \frac{\partial C}{\partial x}] + \frac{\partial}{\partial y}[D_y \cdot \frac{\partial C}{\partial y}] - rC + p_{xy} \]

The parameter \(p_{xy}\) is the production rate, which is zero everywhere except for 50 randomly positioned spots where it is 1.

Transport is performed by ReacTran function tran.2D; the state variable vector (y) is recast in matrix form (CONC) before it is transported. The first-order consumption rate -r * CONC is added to the rate of change due to transport (Tran$dC). Production, p is added to 50 cells indexed by ii, and which are randomly selected from the grid. The function returns a list, containing the derivatives, as a vector:

pde2D <- function (t, y, parms) {
  CONC <- matrix(nr = n, nc = n, y)
  Tran <- tran.2D(CONC, D.x = Dx, D.y = Dy, dx = dx, dy = dy)
  dCONC <- Tran$dC - r * CONC
  dCONC[ii]<- dCONC[ii] + p
  
  return(list(as.vector(dCONC)))
}

Before running the model, the grid sizes (dx, dx), diffusion coefficients (Dx, Dy), 1st order consumption rate (r) are defined. There are 100 boxes in x- and y direction (n). Furthermore, we assume that the substance is produced in 50 randomly chosen cells (ii) at a constant rate (p):

n <- 100
dy <- dx <- 1
Dy <- Dx <- 2
r <- 0.001
p <- runif(50)
ii <- trunc(cbind(runif(50) * n, runif(50) * n) + 1)

The steady-state is found using rootSolve’s function steady.2D. It takes as arguments amongst others the dimensionality of the problem (dimens) and the length of the work array needed by the solver (lrw = 600000). It takes less than 0.5 seconds to solve this 10000 state variable model.

set.seed(2)
Conc0 <- matrix(nr = n, nc = n, 10)
print(system.time(ST <- steady.2D(y = Conc0, func = pde2D, parms = NULL, dimens = c(n, n), lrw = 6e+05)))
   user  system elapsed 
  0.112   0.000   0.112 

Plot using ggplot:

expand_grid(y = seq(0, 1, length.out = 100), x = seq(0, 1, length.out = 100)) %>% # y first then x to match the output of image
  mutate(z = ST$y) %>% 
  ggplot() +
  geom_raster(aes(x, y, fill = z),
             interpolate = TRUE, show.legend = FALSE) +
  scale_x_continuous(breaks = seq(0, 1, 0.2)) +
  scale_y_continuous(breaks = seq(0, 1, 0.2)) +
  scale_fill_distiller(palette = "Spectral") +
  coord_fixed(expand = F) +
  ggtitle("Figure 5: Steady-state solution of the 2-D diffusion-reaction model") + 
  theme(plot.title = element_text(size = 12))

Plot a 3-d version using plotly:

out_3d <-
  list(
    x = seq(0, 1, length.out = 100),
    y = seq(0, 1, length.out = 100),
    z = matrix(ST$y, nrow = 100, byrow = TRUE)
  )

# Plot:
plot_ly(x = out_3d$x, y = out_3d$y, z = out_3d$z, colors = my_palette) %>%
  add_surface() 
LS0tCnRpdGxlOiAiU29sdmluZyBwYXJ0aWFsIGRpZmZlcmVudGlhbCBlcXVhdGlvbnMsIHVzaW5nIGBSYCBwYWNrYWdlIGBSZWFjVHJhbmAiCnN1YnRpdGxlOiAiRGVtb25zdHJhdGlvbiBvZiB0aGUgcGFja2FnZSB3aXRoIHBsb3R0aW5nIGluIGBnZ3Bsb3QyYCBhbmQgYHBsb3RseWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAxCi0tLQoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgbm90ZWJvb2sgaXMgYSBkZW1vbnN0cmF0aW9uIG9mIHRoZSBgUmAgcGFja2FnZSBgUmVhY1RyYW5gLiBJdCBpcyBhIGRpcmVjdCBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgY29kZSBhbmQgbW9kZWxzIGRlc2NyaWJlZCBpbiB0aGlzIFt2aWduZXR0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1JlYWNUcmFuL3ZpZ25ldHRlcy9QREUucGRmKSBwcm92aWRlZCBieSB0aGUgcGFja2FnZSBhdXRob3JzLCAoU29ldGFlcnQgYW5kIE1leXNtYW4pLiBIb3dldmVyIHRoZSBwbG90cyBhcmUgaW1wbGVtZW50ZWQgdXNpbmcgYGdncGxvdDJgLCB3aXRoIGBwbG90bHlgIHVzZWQgZm9yIHNvbWUgMy1kIGV4YW1wbGVzLiBUaGUgcGxvdHRpbmcgZnVuY3Rpb25zIGluY2x1ZGVkIGluIHRoZSBwYWNrYWdlIHVzZSBiYXNlIGBSYC4gVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBkZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIG1vZGVybiBwbG90dGluZyBsaWJyYXJpZXMgd2l0aCB0aGUgb3V0cHV0IG9mIHRoZSBgUmVhY1RyYW5gIHBhY2thZ2UuCgojIFNldHVwCgpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShSZWFjVHJhbikKbGlicmFyeSh0aWR5dmVyc2UpCnRoZW1lX3NldCh0aGVtZV9idygpKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShtZXRSKQpsaWJyYXJ5KHBsb3RseSkKYGBgCgojIDIgQSBwYXJhYm9saWMgUERFCgpBcyBhbiBleGFtcGxlIG9mIHRoZSBwYXJhYm9saWMgdHlwZSwgY29uc2lkZXIgdGhlIDEtRCBkaWZmdXNpb24tcmVhY3Rpb24gbW9kZWwsIGluIHNwaGVyaWNhbCwgY3lsaW5kcmljYWwgYW5kIGNhcnRlc2lhbiBjb29yZGluYXRlcywgZGVmaW5lZCBmb3IgJHIkIGluIFswLCAxMF06CiQkClxmcmFje1xwYXJ0aWFsIEN9e1xwYXJ0aWFsIHR9ID0gXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIHJ9XGxlZnQoRCBcY2RvdCBcZnJhY3tccGFydGlhbCBDfXtccGFydGlhbCByfSBccmlnaHQpIC0gUQokJAp3aXRoICR0JCB0aGUgdGltZSwgJHIkIHRoZSAocmFkaWFsKSBkaXN0YW5jZSBmcm9tIHRoZSBvcmlnaW4sICRRJCwgdGhlIGNvbnN1bXB0aW9uIHJhdGUsIGFuZCB3aXRoIGJvdW5kYXJ5IGNvbmRpdGlvbnMgKHZhbHVlcyBhdCB0aGUgbW9kZWwgZWRnZXMpOgokJApcZnJhY3tccGFydGlhbCBDfXtccGFydGlhbCByfV97cj0wfSA9IDAKJCQKJCQKQ197cj0xMH0gPSBDZXh0CiQkCgpUbyBzb2x2ZSB0aGlzIG1vZGVsIGluIGBSYCwgZmlyc3QgdGhlIDEtRCBtb2RlbCBgR3JpZGAgaXMgZGVmaW5lZDsgaXQgZGl2aWRlcyAxMCBjbSAoYExgKSBpbnRvIDEwMDAgZXF1YWxseS1zaXplZCBib3hlcyAoYE5gKS4KYGBge3J9CkdyaWQgPC0gc2V0dXAuZ3JpZC4xRChOID0gMTAwMCwgTCA9IDEwKQpgYGAKCk5leHQgdGhlIHByb3BlcnRpZXMgYHJgIGFuZCBgcl4yYCBhcmUgZGVmaW5lZCBvbiB0aGlzIGdyaWQ6CmBgYHtyfQpyIDwtIHNldHVwLnByb3AuMUQoZ3JpZCA9IEdyaWQsIGZ1bmMgPSBmdW5jdGlvbihyKSByKQpyMiA8LSBzZXR1cC5wcm9wLjFEKGdyaWQgPSBHcmlkLCBmdW5jID0gZnVuY3Rpb24ocikgcl4yKQpgYGAKClRoZSBtb2RlbCBlcXVhdGlvbiBpbmNsdWRlcyBhIHRyYW5zcG9ydCB0ZXJtLCBhcHByb3hpbWF0ZWQgYnkgYFJlYWNUcmFuYCBmdW5jdGlvbiBgdHJhbi4xRGAgYW5kIGEgY29uc3VtcHRpb24gdGVybSAoYFFgKTsgb25seSB0aGUgZG93bnN0cmVhbSBib3VuZGFyeSBjb25kaXRpb24sIHByZXNjcmliZWQgYXMgYSBjb25jZW50cmF0aW9uIChgQy5kb3duYCkgbmVlZHMgdG8gYmUgc3BlY2lmaWVkLCBhcyB0aGUgemVyby1ncmFkaWVudCBhdCB0aGUgdXBzdHJlYW0gYm91bmRhcnkgaXMgdGhlIGRlZmF1bHQ6CmBgYHtyfQpwZGUxRCA8LSBmdW5jdGlvbih0LCBDLCBwYXJtcywgQSA9IDEpIHsKICB0cmFuIDwtIHRyYW4uMUQoQyA9IEMsIEEgPSBBLCBEID0gRCwgQy5kb3duID0gQ2V4dCwgZHggPSBHcmlkKSRkQwogIAogIGxpc3QodHJhbiAtIFEpCn0KYGBgCgpUaGUgbW9kZWwgcGFyYW1ldGVycyBhcmUgZGVmaW5lZDoKYGBge3J9CkQgPC0gMQpRIDwtIDEKQ2V4dCA8LSAyMApgYGAKCiMjIDIuMS4gU3RlYWR5LXN0YXRlIHNvbHV0aW9uCgpJbiBhIGZpcnN0IGFwcGxpY2F0aW9uLCB0aGUgbW9kZWwgaXMgc29sdmVkIHRvIHN0ZWFkeS1zdGF0ZSwgd2hpY2ggcmV0cmlldmVzIHRoZSBjb25kaXRpb24gd2hlcmUgdGhlIGNvbmNlbnRyYXRpb25zIGFyZSBpbnZhcmlhbnQsIGUuZy4gZm9yIHRoZSBjeWxpbmRyaWNhbCBjb29yZGluYXRlIGNhc2U6CiQkCjAgPSBcZnJhY3sxfXtyXjJ9IFxjZG90IFxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCByfVxsZWZ0KHJeMiBcY2RvdCBEIFxjZG90IFxmcmFje1xwYXJ0aWFsIEN9e1xwYXJ0aWFsIHJ9IFxyaWdodCkgLSBRCiQkCgpJbiBgUmAgLCBzdGVhZHktc3RhdGUgY29uZGl0aW9ucyBjYW4gYmUgZXN0aW1hdGVkIHVzaW5nIGZ1bmN0aW9ucyBmcm9tIHBhY2thZ2UgYHJvb3RTb2x2ZWAgd2hpY2ggaW1wbGVtZW50IGFtb25nc3Qgb3RoZXJzIGEgTmV3dG9uLVJhcGhzb24gYWxnb3JpdGhtLiBGb3IgMS1kaW1lbnNpb25hbCBtb2RlbHMsIGBzdGVhZHkuMURgIHNob3VsZCBiZSB1c2VkLiBUaGUgaW5pdGlhbCAiZ3Vlc3MiIG9mIHRoZSBzdGVhZHktc3RhdGUgc29sdXRpb24gKGB5YCkgaXMgdW5pbXBvcnRhbnQ7IGhlcmUgd2UgdGFrZSBzaW1wbHkgYE5gIHJhbmRvbSBudW1iZXJzLiBBcmd1bWVudCBgbnNwZWMgPSAxYCBpbmZvcm1zIHRoZSBzb2x2ZXIgdGhhdCBvbmx5IG9uZSBjb21wb25lbnQgaXMgZGVzY3JpYmVkOgpgYGB7cn0KbGlicmFyeShyb290U29sdmUpCkNhcnRlc2lhbiA8LSBzdGVhZHkuMUQoeSA9IHJ1bmlmKEdyaWQkTiksIGZ1bmMgPSBwZGUxRCwgcGFybXMgPSBOVUxMLCBuc3BlYyA9IDEsIEEgPSAxKQpDeWxpbmRyaWNhbCA8LSBzdGVhZHkuMUQoeSA9IHJ1bmlmKEdyaWQkTiksIGZ1bmMgPSBwZGUxRCwgcGFybXMgPSBOVUxMLCBuc3BlYyA9IDEsIEEgPSByKQpwcmludChzeXN0ZW0udGltZSgKICBTcGhlcmljYWwgPC0gc3RlYWR5LjFEKHkgPSBydW5pZihHcmlkJE4pLCBmdW5jID0gcGRlMUQsIHBhcm1zID0gTlVMTCwgbnNwZWMgPSAxLCBBID0gcjIpCikpCmBgYAoKUGxvdCB1c2luZyBgZ2dwbG90MmA6CmBgYHtyfQpkYXRhLmZyYW1lKAogIHggPSBHcmlkJHgubWlkLAogIGNhcnRlc2lhbiA9IENhcnRlc2lhbiR5LAogIGN5bGluZHJpY2FsID0gQ3lsaW5kcmljYWwkeSwKICBzcGhlcmljYWwgPSBTcGhlcmljYWwkeQopICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFjb250YWlucygieCIpLCBuYW1lc190byA9ICJjb29yZCIsIHZhbHVlc190byA9ICJ5IikgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHgsIHksIGNvbG9yID0gY29vcmQsIGxpbmV0eXBlID0gY29vcmQpLAogICAgICAgICAgICBzaXplID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAsIDIpKSArCiAgbGFicyh5ID0gIkMiLCBjb2xvciA9IE5VTEwsIGxpbmV0eXBlID0gTlVMTCwKICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxOiBTdGVhZHktc3RhdGUgc29sdXRpb24gb2YgdGhlIDEtRCBkaWZmdXNpb24tcmVhY3Rpb24gbW9kZWwiKSArCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDEsIDApLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAwKSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyAyLjIuIFRoZSBtZXRob2Qgb2YgbGluZXMKCk5leHQgdGhlIG1vZGVsIChmb3Igc3BoZXJpY2FsIGNvb3JkaW5hdGVzKSBpcyBydW4gZHluYW1pY2FsbHkgZm9yIDEwMCB0aW1lIHVuaXRzIHVzaW5nIGBkZVNvbHZlYCBmdW5jdGlvbiBgb2RlLjFEYCwgYW5kIHN0YXJ0aW5nIHdpdGggYW4gaW5pdGlhbGx5IHVuaWZvcm0gZGlzdHJpYnV0aW9uIChgeSA9IHJlcCgxLCBHcmlkJE4pYCk6CmBgYHtyfQpyZXF1aXJlKGRlU29sdmUpCnRpbWVzIDwtIHNlcSgwLCAxMDAsIGJ5ID0gMSkKc3lzdGVtLnRpbWUoCiAgb3V0IDwtIG9kZS4xRCh5ID0gcmVwKDEsIEdyaWQkTiksIHRpbWVzID0gdGltZXMsIGZ1bmMgPSBwZGUxRCwgcGFybXMgPSBOVUxMLCBuc3BlYyA9IDEsIEEgPSByMikKKQpgYGAKCkhlcmUsIGBvdXRgIGlzIGEgbWF0cml4LCB3aG9zZSAxc3QgY29sdW1uIGNvbnRhaW5zIHRoZSBvdXRwdXQgdGltZXMsIGFuZCB0aGUgbmV4dCBjb2x1bW5zIHRoZSB2YWx1ZXMgb2YgdGhlIHN0YXRlIHZhcmlhYmxlcyBpbiB0aGUgZGlmZmVyZW50IGJveGVzOgpgYGB7cn0KdGFpbChvdXRbLCAxOjRdLCBuID0gMykKYGBgCgpQbG90IHVzaW5nIGBnZ3Bsb3QyYDoKYGBge3J9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBncmlkIHZhbHVlcyBmb3Igam9pbmluZyB0byB0aGUgb3V0cHV0OgpncmlkX3gubWlkIDwtIGRhdGEuZnJhbWUoCiAgWCA9IHBhc3RlMCgiWCIsIDE6bGVuZ3RoKEdyaWQkeC5taWQpKSwKICBncmlkID0gR3JpZCR4Lm1pZAopCgojIFBpdm90IHRoZSBvdXRwdXQgdG8gbG9uZ2VyIGZvcm1hdCBhbmQgam9pbiB3aXRoIHRoZSBhYm92ZSBkYXRhIGZyYW1lIHRvIGFkZCB0aGUgRGlzdGFuY2UgdmFsdWVzOgpvdXRfbG9uZyA8LSBkYXRhLmZyYW1lKG91dCkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIlgiKSwgbmFtZXNfdG8gPSAiWCIsIHZhbHVlc190byA9ICJkZW5zaXR5IikgJT4lCiAgbGVmdF9qb2luKGdyaWRfeC5taWQsIGJ5ID0gIlgiKQoKIyBQbG90OgpnZ3Bsb3Qob3V0X2xvbmcpICsKICBnZW9tX3Jhc3RlcihhZXModGltZSwgZ3JpZCwgZmlsbCA9IGRlbnNpdHkpLAogICAgICAgICAgICAgaW50ZXJwb2xhdGUgPSBUUlVFLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9jb250b3VyKGFlcyh0aW1lLCBncmlkLCB6ID0gZGVuc2l0eSksIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX3RleHRfY29udG91cihhZXModGltZSwgZ3JpZCwgeiA9IGRlbnNpdHkpLCBza2lwID0gMCwgbnVkZ2VfeSA9IDAuMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAyMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwLCAyKSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiU3BlY3RyYWwiLAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjIpKSArCiAgbGFicyh4ID0gIlRpbWUgKGRheXMpIiwgeSA9ICJEaXN0YW5jZSAoY20pIiwgZmlsbCA9IE5VTEwsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMjogRHluYW1pYyBzb2x1dGlvbiBvZiB0aGUgMS1EIGRpZmZ1c2lvbi1yZWFjdGlvbiBtb2RlbCIpICsKICBjb29yZF9lcXVhbChleHBhbmQgPSBGLCByYXRpbyA9IDEwKQpgYGAKCkFkZCBhIDMtZCBwbG90IHVzaW5nIGBwbG90bHlgOgpgYGB7cn0Kb3V0XzNkIDwtCiAgbGlzdCgKICAgIHggPSB0aW1lcywKICAgIHkgPSBncmlkX3gubWlkLAogICAgeiA9IHQob3V0WyAsIC0xXSkpICMgbmVlZCB0byB0cmFuc3Bvc2UgdGhpcyB0byBtYXRjaCB0aGUgeCBhcyB0aW1lcyBhbmQgeSBhcyByCgojIFBsb3Q6Cm15X3BhbGV0dGUgPC0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDksICJTcGVjdHJhbCIpKSg0MCkpICMgQ29sb3JCcmV3ZXIgU3BlY3RyYWwgcGFsZXR0ZQpwbG90X2x5KHggPSBvdXRfM2QkeCwgeSA9IG91dF8zZCR5LCB6ID0gb3V0XzNkJHosIGNvbG9ycyA9IG15X3BhbGV0dGUpICU+JQogIGFkZF9zdXJmYWNlKCkgJT4lCiAgbGF5b3V0KAogICAgc2NlbmUgPSBsaXN0KAogICAgICBjYW1lcmE9bGlzdCgKICAgICAgICBleWUgPSBsaXN0KHg9MCwgeT0tMiwgej0xKQogICAgICAgICkKICAgICkKICApCmBgYAoKIyAzIEEgaHlwZXJib2xpYyBQREUKClRoZSBlcXVhdGlvbiBmb3IgYSB3YXZlIHRyYXZlbGxpbmcgaW4gb25lIGRpcmVjdGlvbiAoJHgkKSBpcyBnaXZlbiBieToKJCQKXGZyYWN7XHBhcnRpYWwgXjIgdX17XHBhcnRpYWwgdF4yfSA9IGNeMiBcZnJhY3tccGFydGlhbCBeMiB1fXtccGFydGlhbCB4XjJ9CiQkCgp3aGVyZSAkYyQgaXMgdGhlIHByb3BhZ2F0aW9uIHNwZWVkIG9mIHRoZSB3YXZlLCBhbmQgJHUkIGlzIHRoZSB2YXJpYWJsZSB0aGF0IGNoYW5nZXMgYXMgdGhlIHdhdmUgcGFzc2VzLiBUaGlzIGVxdWF0aW9uIGlzIHNlY29uZC1vcmRlciBpbiBib3RoICR0JCBhbmQgJHgkLiBUaGUgd2F2ZSBlcXVhdGlvbiBpcyB0aGUgcHJvdG90eXBlIG9mIGEgImh5cGVyYm9saWMiIHBhcnRpYWwgZGlmZmVyZW50aWFsIGVxdWF0aW9uLiAKCkZvciBpdCB0byBiZSBzb2x2ZWQgaW4gYFJgLCB0aGUgZXF1YXRpb24gaXMgcmV3cml0dGVuIGFzIHR3byBjb3VwbGVkIGVxdWF0aW9ucywgZmlyc3Qtb3JkZXIgaW4gdGltZToKCiQkClxmcmFje1xwYXJ0aWFsIHVfMX17XHBhcnRpYWwgdH0gPSB1XzIKJCQKCiQkClxmcmFje1xwYXJ0aWFsIHVfMn17XHBhcnRpYWwgdH0gPSBjXjIgXGZyYWN7XHBhcnRpYWwgXjIgdV8xfXtccGFydGlhbCB4XjJ9CiQkCgpXZSBzb2x2ZSB0aGUgZXF1YXRpb24gd2l0aCB0aGUgZm9sbG93aW5nIGluaXRpYWwgYW5kIGJvdW5kYXJ5IGNvbmRpdGlvbnM6CiQkCnVfMSgwLCB4KSA9IFxleHBeey0wLjA1IHheMn0KJCQKJCQKdV8yKDAsIHgpID0gMAokJAokJAp1X3t0LCAtIFxpbmZ0eX0gPSAwCiQkCiQkCnVfe3QsIFxpbmZ0eX0gPSAwCiQkCndoZXJlIHRoZSBmaXJzdCBjb25kaXRpb24gcmVwcmVzZW50cyBhIEdhdXNzaWFuIHB1bHNlLgoKSGVyZSwgdGhlIGdyaWQgZXh0ZW5kcyBmcm9tIC0xMDAgdG8gMTAwOgpgYGB7cn0KZHggPC0gMC4yCnhncmlkIDwtIHNldHVwLmdyaWQuMUQoLTEwMCwgMTAwLCBkeC4xID0gZHgpCnggPC0geGdyaWQkeC5taWQKTiA8LSB4Z3JpZCROCmBgYAoKVGhlIGluaXRpYWwgY29uZGl0aW9uLCBgeWluaWAgYW5kIG91dHB1dCBgdGltZXNgIGFyZSBkZWZpbmVkIG5leHQ6CmBgYHtyfQp1aW5pIDwtIGV4cCgtMC4wNSAqIHheMikKdmluaSA8LSByZXAoMCwgTikKeWluaSA8LSBjKHVpbmksIHZpbmkpCnRpbWVzIDwtIHNlcSAoZnJvbSA9IDAsIHRvID0gNTAsIGJ5ID0gMSkKYGBgCgpUaGUgd2F2ZSBlcXVhdGlvbiBkZXJpdmF0aXZlIGZ1bmN0aW9uIGZpcnN0IGV4dHJhY3RzLCBmcm9tIHN0YXRlIHZhcmlhYmxlIHZlY3RvciBgeWAgdGhlIHR3byBwcm9wZXJ0aWVzIGB1MWAsIGB1MmAsIGJvdGggb2YgbGVuZ3RoIGBOYCwgYWZ0ZXIgd2hpY2ggYFJlYWNUcmFuYCBmdW5jdGlvbiBgdHJhbi4xRGAgcGVyZm9ybXMgdHJhbnNwb3J0IG9mIGB1MWAuIFRoZSBzcXVhcmVkIHZlbG9jaXR5ICgkY14yJCkgaXMgdGFrZW4gYXMgMSAoYEQgPSAxYCk6CmBgYHtyfQp3YXZlIDwtIGZ1bmN0aW9uICh0LCB5LCBwYXJtcykgewogIHUxIDwtIHlbMTpOXQogIHUyIDwtIHlbLSgxOk4pXQogIGR1MSA8LSB1MgogIGR1MiA8LSB0cmFuLjFEKEMgPSB1MSwgQy51cCA9IDAsIEMuZG93biA9IDAsIEQgPSAxLCBkeCA9IHhncmlkKSRkQwogIAogIHJldHVybihsaXN0KGMoZHUxLCBkdTIpKSkKfQpgYGAKClRoZSB3YXZlIGVxdWF0aW9uIGNhbiBiZSBzb2x2ZWQgZWZmaWNpZW50bHkgd2l0aCBhIG5vbi1zdGlmZiBzb2x2ZXIgc3VjaCBhcyB0aGUgUnVuZ2UtS3V0dGEgbWV0aG9kIGBvZGU0NWA6CmBgYHtyfQpvdXQgPC0gb2RlLjFEKGZ1bmMgPSB3YXZlLCB5ID0geWluaSwgdGltZXMgPSB0aW1lcywgcGFybXMgPSBOVUxMLCBuc3BlYyA9IDIsIG1ldGhvZCA9ICJvZGU0NSIsIGRpbWVucyA9IE4sIG5hbWVzID0gYygidSIsICJ2IikpCmBgYAoKUGxvdCB1c2luZyBgZ2dwbG90MmA6CmBgYHtyfQppb3V0IDwtIHNlcSgxMSwgbGVuZ3RoKHRpbWVzKSwgYnkgPSAxMCkKZGF0YS5mcmFtZShvdXRbYygxLCBpb3V0KSwgMTooTisxKV0pICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGNvbnRhaW5zKCJYIiksIG5hbWVzX3RvID0gIlgiLCB2YWx1ZXNfdG8gPSAidSIpICU+JSAKICBtdXRhdGUoeCA9IHJlcCh4LCA2KSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHgsIHUsIGNvbG9yID0gZmFjdG9yKHRpbWUpLCBsaW5ldHlwZSA9IGZhY3Rvcih0aW1lKSkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC00MCwgNDAsIDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgMC4yKSkgKwogIGdnc2NpOjpzY2FsZV9jb2xvcl9kMyhuYW1lID0gInRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEsIGlvdXQpIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKCJ0ID0gIiwgYygxLCBpb3V0KSAtIDEpKSArCiAgc2NhbGVfbGluZXR5cGVfZGlzY3JldGUobmFtZSA9ICJ0aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEsIGlvdXQpIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoInQgPSAiLCBjKDEsIGlvdXQpIC0gMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUwLCA1MCkpICsKICBnZ3RpdGxlKCJGaWd1cmUgMzogVGhlIDEtRCB3YXZlIGVxdWF0aW9uIikgKwogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKUGxvdCBhIDMtZCB2ZXJzaW9uIHVzaW5nIGBwbG90bHlgOgpgYGB7cn0Kb3V0XzNkIDwtCiAgbGlzdCgKICAgIHggPSB0aW1lcywKICAgIHkgPSBncmlkX3gubWlkLAogICAgeiA9IHQob3V0WywgMjooTisxKV0pKSAjIG5lZWQgdG8gdHJhbnNwb3NlIHRoaXMgdG8gbWF0Y2ggdGhlIHggYXMgdGltZXMgYW5kIHkgYXMgcgoKIyBQbG90OgpwbG90X2x5KHggPSBvdXRfM2QkeCwgeSA9IG91dF8zZCR5LCB6ID0gb3V0XzNkJHosIGNvbG9ycyA9IG15X3BhbGV0dGUpICU+JQogIGFkZF9zdXJmYWNlKCkKYGBgCgojIDQgQW4gZWxsaXB0aWMgUERFCgpUaGUgZmluYWwgZXhhbXBsZSBkZXNjcmliZXMgYWdhaW4gYSBkaWZmdXNpb24tcmVhY3Rpb24gc3lzdGVtIHdpdGggcHJvZHVjdGlvbiAkcCQsIGNvbnN1bXB0aW9uICRyQyQsIGFuZCBkaWZmdXNpdmUgdHJhbnNwb3J0IChkaWZmdXNpb24gY29lZmZpY2llbnRzICREeCQsICREeSQpIG9mIGEgc3Vic3RhbmNlICRDJCBpbiAyIGRpbWVuc2lvbnMgJCh4LCB5KSQ7IHRoZSBib3VuZGFyaWVzIGFyZSBwcmVzY3JpYmVkIGFzIHplcm8tZ3JhZGllbnQgKHRoZSBkZWZhdWx0KToKJCQKXGZyYWN7XHBhcnRpYWwgQ317XHBhcnRpYWwgdH0gPSBcZnJhY3tccGFydGlhbH17XHBhcnRpYWwgeH1bRF94IFxjZG90IFxmcmFje1xwYXJ0aWFsIEN9e1xwYXJ0aWFsIHh9XSArIFxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCB5fVtEX3kgXGNkb3QgXGZyYWN7XHBhcnRpYWwgQ317XHBhcnRpYWwgeX1dIC0gckMgKyBwX3t4eX0KJCQKClRoZSBwYXJhbWV0ZXIgJHBfe3h5fSQgaXMgdGhlIHByb2R1Y3Rpb24gcmF0ZSwgd2hpY2ggaXMgemVybyBldmVyeXdoZXJlIGV4Y2VwdCBmb3IgNTAgcmFuZG9tbHkgcG9zaXRpb25lZCBzcG90cyB3aGVyZSBpdCBpcyAxLgoKVHJhbnNwb3J0IGlzIHBlcmZvcm1lZCBieSBgUmVhY1RyYW5gIGZ1bmN0aW9uIGB0cmFuLjJEYDsgdGhlIHN0YXRlIHZhcmlhYmxlIHZlY3RvciAoYHlgKSBpcyByZWNhc3QgaW4gbWF0cml4IGZvcm0gKGBDT05DYCkgYmVmb3JlIGl0IGlzIHRyYW5zcG9ydGVkLiBUaGUgZmlyc3Qtb3JkZXIgY29uc3VtcHRpb24gcmF0ZSBgLXIgKiBDT05DYCBpcyBhZGRlZCB0byB0aGUgcmF0ZSBvZiBjaGFuZ2UgZHVlIHRvIHRyYW5zcG9ydCAoYFRyYW4kZENgKS4gUHJvZHVjdGlvbiwgYHBgIGlzIGFkZGVkIHRvIDUwIGNlbGxzIGluZGV4ZWQgYnkgYGlpYCwgYW5kIHdoaWNoIGFyZSByYW5kb21seSBzZWxlY3RlZCBmcm9tIHRoZSBncmlkLiBUaGUgZnVuY3Rpb24gcmV0dXJucyBhIGxpc3QsIGNvbnRhaW5pbmcgdGhlIGRlcml2YXRpdmVzLCBhcyBhIHZlY3RvcjoKYGBge3J9CnBkZTJEIDwtIGZ1bmN0aW9uICh0LCB5LCBwYXJtcykgewogIENPTkMgPC0gbWF0cml4KG5yID0gbiwgbmMgPSBuLCB5KQogIFRyYW4gPC0gdHJhbi4yRChDT05DLCBELnggPSBEeCwgRC55ID0gRHksIGR4ID0gZHgsIGR5ID0gZHkpCiAgZENPTkMgPC0gVHJhbiRkQyAtIHIgKiBDT05DCiAgZENPTkNbaWldPC0gZENPTkNbaWldICsgcAogIAogIHJldHVybihsaXN0KGFzLnZlY3RvcihkQ09OQykpKQp9CmBgYAoKQmVmb3JlIHJ1bm5pbmcgdGhlIG1vZGVsLCB0aGUgZ3JpZCBzaXplcyAoYGR4YCwgYGR4YCksIGRpZmZ1c2lvbiBjb2VmZmljaWVudHMgKGBEeGAsIGBEeWApLCAxc3Qgb3JkZXIgY29uc3VtcHRpb24gcmF0ZSAoYHJgKSBhcmUgZGVmaW5lZC4gVGhlcmUgYXJlIDEwMCBib3hlcyBpbiBgeGAtIGFuZCBgeWAgZGlyZWN0aW9uIChgbmApLiBGdXJ0aGVybW9yZSwgd2UgYXNzdW1lIHRoYXQgdGhlIHN1YnN0YW5jZSBpcyBwcm9kdWNlZCBpbiA1MCByYW5kb21seSBjaG9zZW4gY2VsbHMgKGBpaWApIGF0IGEgY29uc3RhbnQgcmF0ZSAoYHBgKToKYGBge3J9Cm4gPC0gMTAwCmR5IDwtIGR4IDwtIDEKRHkgPC0gRHggPC0gMgpyIDwtIDAuMDAxCnAgPC0gcnVuaWYoNTApCmlpIDwtIHRydW5jKGNiaW5kKHJ1bmlmKDUwKSAqIG4sIHJ1bmlmKDUwKSAqIG4pICsgMSkKYGBgCgpUaGUgc3RlYWR5LXN0YXRlIGlzIGZvdW5kIHVzaW5nIGByb290U29sdmVgJ3MgZnVuY3Rpb24gYHN0ZWFkeS4yRGAuIEl0IHRha2VzIGFzIGFyZ3VtZW50cyBhbW9uZ3N0IG90aGVycyB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlIHByb2JsZW0gKGBkaW1lbnNgKSBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgd29yayBhcnJheSBuZWVkZWQgYnkgdGhlIHNvbHZlciAoYGxydyA9IDYwMDAwMGApLiBJdCB0YWtlcyBsZXNzIHRoYW4gMC41IHNlY29uZHMgdG8gc29sdmUgdGhpcyAxMDAwMCBzdGF0ZSB2YXJpYWJsZSBtb2RlbC4KYGBge3J9CnNldC5zZWVkKDIpCkNvbmMwIDwtIG1hdHJpeChuciA9IG4sIG5jID0gbiwgMTApCnByaW50KHN5c3RlbS50aW1lKFNUIDwtIHN0ZWFkeS4yRCh5ID0gQ29uYzAsIGZ1bmMgPSBwZGUyRCwgcGFybXMgPSBOVUxMLCBkaW1lbnMgPSBjKG4sIG4pLCBscncgPSA2ZSswNSkpKQpgYGAKClBsb3QgdXNpbmcgYGdncGxvdGA6CmBgYHtyfQpleHBhbmRfZ3JpZCh5ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApLCB4ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApKSAlPiUgIyB5IGZpcnN0IHRoZW4geCB0byBtYXRjaCB0aGUgb3V0cHV0IG9mIGltYWdlCiAgbXV0YXRlKHogPSBTVCR5KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcmFzdGVyKGFlcyh4LCB5LCBmaWxsID0geiksCiAgICAgICAgICAgICBpbnRlcnBvbGF0ZSA9IFRSVUUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIDAuMikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIDAuMikpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogIGNvb3JkX2ZpeGVkKGV4cGFuZCA9IEYpICsKICBnZ3RpdGxlKCJGaWd1cmUgNTogU3RlYWR5LXN0YXRlIHNvbHV0aW9uIG9mIHRoZSAyLUQgZGlmZnVzaW9uLXJlYWN0aW9uIG1vZGVsIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCmBgYAoKUGxvdCBhIDMtZCB2ZXJzaW9uIHVzaW5nIGBwbG90bHlgOgpgYGB7cn0Kb3V0XzNkIDwtCiAgbGlzdCgKICAgIHggPSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDEwMCksCiAgICB5ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApLAogICAgeiA9IG1hdHJpeChTVCR5LCBucm93ID0gMTAwLCBieXJvdyA9IFRSVUUpCiAgKQoKIyBQbG90OgpwbG90X2x5KHggPSBvdXRfM2QkeCwgeSA9IG91dF8zZCR5LCB6ID0gb3V0XzNkJHosIGNvbG9ycyA9IG15X3BhbGV0dGUpICU+JQogIGFkZF9zdXJmYWNlKCkgCmBgYA==